#pragma once
#include "log/channel.hpp"
#include "log/message.hpp"
#include "log/formatter.hpp"
#include "stdlib/bugcheck.hpp"
#include "stdlib/exception.hpp"
using namespace JHCPP::stdlib;
using namespace JHCPP::stdlib::jhException;
#include <string>
#include <map>
#include <vector>
#include <cstddef>

namespace JHCPP
{
	namespace log
	{
		class CLogger : public CChannel
		{
		public:
			const std::string& name() const;

			void setChannel(CChannel* pChannel);
			CChannel* getChannel() const;

			void setLevel(int level);
			int getLevel() const;
			void setLevel(const std::string& level);
			void setProperty(const std::string& name, const std::string& value);

			void log(const CMessage& msg);
			void log(const CException& exc);
			void log(const CException& exc, const char* file, int line);

			void fatal(const std::string& msg);
			void fatal(const std::string& msg, const char* file, int line);
			void critical(const std::string& msg);
			void critical(const std::string& msg, const char* file, int line);
			void error(const std::string& msg);
			void error(const std::string& msg, const char* file, int line);
			void warning(const std::string& msg);
			void warning(const std::string& msg, const char* file, int line);
			void notice(const std::string& msg);
			void notice(const std::string& msg, const char* file, int line);
			void information(const std::string& msg);
			void information(const std::string& msg, const char* file, int line);
			void debug(const std::string& msg);
			void debug(const std::string& msg, const char* file, int line);
			void trace(const std::string& msg);
			void trace(const std::string& msg, const char* file, int line);
			void dump(const std::string& msg, const void* buffer, std::size_t length, CMessage::Priority prio = CMessage::PRIO_DEBUG);

			bool is(int level) const;
			bool fatal() const;
			bool critical() const;
			bool error() const;
			bool warning() const;
			bool notice() const;
			bool information() const;
			bool debug() const;
			bool trace() const;

			static std::string format(const std::string& fmt, const std::string& arg);
			static std::string format(const std::string& fmt, const std::string& arg0, const std::string& arg1);
			static std::string format(const std::string& fmt, const std::string& arg0, const std::string& arg1, const std::string& arg2);	
			static std::string format(const std::string& fmt, const std::string& arg0, const std::string& arg1, const std::string& arg2, const std::string& arg3);	

			static void formatDump(std::string& message, const void* buffer, std::size_t length);

			static void setLevel(const std::string& name, int level);
			static void setChannel(const std::string& name, CChannel* pChannel);
			static void setProperty(const std::string& loggerName, const std::string& propertyName, const std::string& value);
	
			static CLogger& get(const std::string& name);
			static CLogger& unsafeGet(const std::string& name);
			static CLogger& create(const std::string& name, CChannel* pChannel, int level = CMessage::PRIO_INFORMATION);
			static CLogger& root();
			static CLogger* has(const std::string& name);

			static void destroy(const std::string& name);
			static void shutdown();
			static void names(std::vector<std::string>& names);
			static int parseLevel(const std::string& level);
			static const std::string ROOT;	

		protected:
			typedef std::map<std::string, CLogger*> LoggerMap;

			CLogger(const std::string& name, CChannel* pChannel, int level);
			~CLogger();

			void log(const std::string& text, CMessage::Priority prio);
			void log(const std::string& text, CMessage::Priority prio, const char* file, int line);

			static std::string format(const std::string& fmt, int argc, std::string argv[]);
			static CLogger& parent(const std::string& name);
			static void add(CLogger* pLogger);
			static CLogger* find(const std::string& name);

		private:
			CLogger();
			CLogger(const CLogger&);
			CLogger& operator = (const CLogger&);

			std::string			m_name;
			CChannel*			m_pChannel;
			int					m_level;

			static LoggerMap*	m_pLoggerMap;
			static CMutex		m_mapMtx;
		};


		//
		// convenience macros
		//
		#define jh_fatal(logger, msg) \
			if ((logger).fatal()) (logger).fatal(msg, __FILE__, __LINE__); else (void) 0

		#define jh_fatal_f1(logger, fmt, arg1) \
		if ((logger).fatal()) (logger).fatal(utils::format((fmt), arg1), __FILE__, __LINE__); else (void) 0

		#define jh_fatal_f2(logger, fmt, arg1, arg2) \
			if ((logger).fatal()) (logger).fatal(utils::format((fmt), (arg1), (arg2)), __FILE__, __LINE__); else (void) 0

		#define jh_fatal_f3(logger, fmt, arg1, arg2, arg3) \
			if ((logger).fatal()) (logger).fatal(utils::format((fmt), (arg1), (arg2), (arg3)), __FILE__, __LINE__); else (void) 0

		#define jh_fatal_f4(logger, fmt, arg1, arg2, arg3, arg4) \
			if ((logger).fatal()) (logger).fatal(utils::format((fmt), (arg1), (arg2), (arg3), (arg4)), __FILE__, __LINE__); else (void) 0

		#define jh_critical(logger, msg) \
			if ((logger).critical()) (logger).critical(msg, __FILE__, __LINE__); else (void) 0

		#define jh_critical_f1(logger, fmt, arg1) \
			if ((logger).critical()) (logger).critical(utils::format((fmt), (arg1)), __FILE__, __LINE__); else (void) 0

		#define jh_critical_f2(logger, fmt, arg1, arg2) \
			if ((logger).critical()) (logger).critical(utils::format((fmt), (arg1), (arg2)), __FILE__, __LINE__); else (void) 0

		#define jh_critical_f3(logger, fmt, arg1, arg2, arg3) \
			if ((logger).critical()) (logger).critical(utils::format((fmt), (arg1), (arg2), (arg3)), __FILE__, __LINE__); else (void) 0

		#define jh_critical_f4(logger, fmt, arg1, arg2, arg3, arg4) \
			if ((logger).critical()) (logger).critical(utils::format((fmt), (arg1), (arg2), (arg3), (arg4)), __FILE__, __LINE__); else (void) 0

		#define jh_error(logger, msg) \
			if ((logger).error()) (logger).error(msg, __FILE__, __LINE__); else (void) 0

		#define jh_error_f1(logger, fmt, arg1) \
			if ((logger).error()) (logger).error(utils::format((fmt), (arg1)), __FILE__, __LINE__); else (void) 0

		#define jh_error_f2(logger, fmt, arg1, arg2) \
			if ((logger).error()) (logger).error(utils::format((fmt), (arg1), (arg2)), __FILE__, __LINE__); else (void) 0

		#define jh_error_f3(logger, fmt, arg1, arg2, arg3) \
			if ((logger).error()) (logger).error(utils::format((fmt), (arg1), (arg2), (arg3)), __FILE__, __LINE__); else (void) 0

		#define jh_error_f4(logger, fmt, arg1, arg2, arg3, arg4) \
			if ((logger).error()) (logger).error(utils::format((fmt), (arg1), (arg2), (arg3), (arg4)), __FILE__, __LINE__); else (void) 0

		#define jh_warning(logger, msg) \
			if ((logger).warning()) (logger).warning(msg, __FILE__, __LINE__); else (void) 0

		#define jh_warning_f1(logger, fmt, arg1) \
			if ((logger).warning()) (logger).warning(utils::format((fmt), (arg1)), __FILE__, __LINE__); else (void) 0

		#define jh_warning_f2(logger, fmt, arg1, arg2) \
			if ((logger).warning()) (logger).warning(utils::format((fmt), (arg1), (arg2)), __FILE__, __LINE__); else (void) 0

		#define jh_warning_f3(logger, fmt, arg1, arg2, arg3) \
			if ((logger).warning()) (logger).warning(utils::format((fmt), (arg1), (arg2), (arg3)), __FILE__, __LINE__); else (void) 0

		#define jh_warning_f4(logger, fmt, arg1, arg2, arg3, arg4) \
			if ((logger).warning()) (logger).warning(utils::format((fmt), (arg1), (arg2), (arg3), (arg4)), __FILE__, __LINE__); else (void) 0

		#define jh_notice(logger, msg) \
			if ((logger).notice()) (logger).notice(msg, __FILE__, __LINE__); else (void) 0

		#define jh_notice_f1(logger, fmt, arg1) \
			if ((logger).notice()) (logger).notice(utils::format((fmt), (arg1)), __FILE__, __LINE__); else (void) 0

		#define jh_notice_f2(logger, fmt, arg1, arg2) \
			if ((logger).notice()) (logger).notice(utils::format((fmt), (arg1), (arg2)), __FILE__, __LINE__); else (void) 0

		#define jh_notice_f3(logger, fmt, arg1, arg2, arg3) \
			if ((logger).notice()) (logger).notice(utils::format((fmt), (arg1), (arg2), (arg3)), __FILE__, __LINE__); else (void) 0

		#define jh_notice_f4(logger, fmt, arg1, arg2, arg3, arg4) \
			if ((logger).notice()) (logger).notice(utils::format((fmt), (arg1), (arg2), (arg3), (arg4)), __FILE__, __LINE__); else (void) 0

		#define jh_information(logger, msg) \
			if ((logger).information()) (logger).information(msg, __FILE__, __LINE__); else (void) 0

		#define jh_information_f1(logger, fmt, arg1) \
			if ((logger).information()) (logger).information(utils::format((fmt), (arg1)), __FILE__, __LINE__); else (void) 0

		#define jh_information_f2(logger, fmt, arg1, arg2) \
			if ((logger).information()) (logger).information(utils::format((fmt), (arg1), (arg2)), __FILE__, __LINE__); else (void) 0

		#define jh_information_f3(logger, fmt, arg1, arg2, arg3) \
			if ((logger).information()) (logger).information(utils::format((fmt), (arg1), (arg2), (arg3)), __FILE__, __LINE__); else (void) 0

		#define jh_information_f4(logger, fmt, arg1, arg2, arg3, arg4) \
			if ((logger).information()) (logger).information(utils::format((fmt), (arg1), (arg2), (arg3), (arg4)), __FILE__, __LINE__); else (void) 0

	#if defined(_DEBUG)
		#define jh_debug(logger, msg) \
			if ((logger).debug()) (logger).debug(msg, __FILE__, __LINE__); else (void) 0

		#define jh_debug_f1(logger, fmt, arg1) \
			if ((logger).debug()) (logger).debug(utils::format((fmt), (arg1)), __FILE__, __LINE__); else (void) 0

		#define jh_debug_f2(logger, fmt, arg1, arg2) \
			if ((logger).debug()) (logger).debug(utils::format((fmt), (arg1), (arg2)), __FILE__, __LINE__); else (void) 0

		#define jh_debug_f3(logger, fmt, arg1, arg2, arg3) \
			if ((logger).debug()) (logger).debug(utils::format((fmt), (arg1), (arg2), (arg3)), __FILE__, __LINE__); else (void) 0

		#define jh_debug_f4(logger, fmt, arg1, arg2, arg3, arg4) \
			if ((logger).debug()) (logger).debug(utils::format((fmt), (arg1), (arg2), (arg3), (arg4)), __FILE__, __LINE__); else (void) 0

		#define jh_trace(logger, msg) \
			if ((logger).trace()) (logger).trace(msg, __FILE__, __LINE__); else (void) 0

		#define jh_trace_f1(logger, fmt, arg1) \
			if ((logger).trace()) (logger).trace(utils::format((fmt), (arg1)), __FILE__, __LINE__); else (void) 0

		#define jh_trace_f2(logger, fmt, arg1, arg2) \
			if ((logger).trace()) (logger).trace(utils::format((fmt), (arg1), (arg2)), __FILE__, __LINE__); else (void) 0

		#define jh_trace_f3(logger, fmt, arg1, arg2, arg3) \
			if ((logger).trace()) (logger).trace(utils::format((fmt), (arg1), (arg2), (arg3)), __FILE__, __LINE__); else (void) 0

		#define jh_trace_f4(logger, fmt, arg1, arg2, arg3, arg4) \
			if ((logger).trace()) (logger).trace(utils::format((fmt), (arg1), (arg2), (arg3), (arg4)), __FILE__, __LINE__); else (void) 0
	#else
		#define jh_debug(logger, msg)
		#define jh_debug_f1(logger, fmt, arg1)
		#define jh_debug_f2(logger, fmt, arg1, arg2)
		#define jh_debug_f3(logger, fmt, arg1, arg2, arg3)
		#define jh_debug_f4(logger, fmt, arg1, arg2, arg3, arg4)
		#define jh_trace(logger, msg)
		#define jh_trace_f1(logger, fmt, arg1)
		#define jh_trace_f2(logger, fmt, arg1, arg2)
		#define jh_trace_f3(logger, fmt, arg1, arg2, arg3)
		#define jh_trace_f4(logger, fmt, arg1, arg2, arg3, arg4)
	#endif

	}//end of namespace log
}//end of namespace JHCPP
